Ext.ns('Ext.ux.Bike.grid');
/**
 * @class Ext.ux.Bike.grid.PropertyColumnModel
 * @extends Ext.grid.ColumnModel A custom column model for the
 *          {@link Ext.ux.Bike.grid.PropertyGrid}. Generally it should not need
 *          to be used directly. Customized to make name column unsortable
 * @constructor
 * @param {Ext.grid.Grid}
 *            grid The grid this store will be bound to
 * @param {Object}
 *            source The source data config object
 */
Ext.ux.Bike.grid.PropertyColumnModel = Ext.extend(Ext.grid.ColumnModel, {
	// private - strings used for locale support
	nameText : 'Name',
	valueText : 'Value',
	dateFormat : 'm/j/Y',
	trueText : 'true',
	falseText : 'false',

	constructor : function(grid, store) {
		var g = Ext.grid, f = Ext.form;

		this.grid = grid;
		g.PropertyColumnModel.superclass.constructor.call(this, [{
							header : this.nameText,
							width : 50,
							sortable : false,
							dataIndex : 'name',
							id : 'name',
							menuDisabled : true
						}, {
							header : this.valueText,
							width : 50,
							resizable : false,
							dataIndex : 'value',
							id : 'value',
							menuDisabled : true
						}]);
		this.store = store;

		var bfield = new f.Field({
					autoCreate : {
						tag : 'select',
						children : [{
									tag : 'option',
									value : 'true',
									html : this.trueText
								}, {
									tag : 'option',
									value : 'false',
									html : this.falseText
								}]
					},
					getValue : function() {
						return this.el.dom.value == 'true';
					}
				});
		this.editors = {
			'date' : new g.GridEditor(new f.DateField({
						selectOnFocus : true
					})),
			'string' : new g.GridEditor(new f.TextField({
						selectOnFocus : true
					})),
			'number' : new g.GridEditor(new f.NumberField({
						selectOnFocus : true,
						style : 'text-align:left;'
					})),
			'boolean' : new g.GridEditor(bfield, {
						autoSize : 'both'
					})
		};
		this.renderCellDelegate = this.renderCell.createDelegate(this);
		this.renderPropDelegate = this.renderProp.createDelegate(this);
	},

	// private
	renderDate : function(dateVal) {
		return dateVal.dateFormat(this.dateFormat);
	},

	// private
	renderBool : function(bVal) {
		return this[bVal ? 'trueText' : 'falseText'];
	},

	// private
	isCellEditable : function(colIndex, rowIndex) {
		return colIndex == 1;
	},

	// private
	getRenderer : function(col) {
		return col == 1 ? this.renderCellDelegate : this.renderPropDelegate;
	},

	// private
	renderProp : function(v) {
		return this.getPropertyName(v);
	},

	// private
	renderCell : function(val, meta, rec) {
		var renderer = this.grid.customRenderers[rec.get('name')];
		if (renderer) {
			return renderer.apply(this, arguments);
		}
		var rv = val;
		if (Ext.isDate(val)) {
			rv = this.renderDate(val);
		} else if (typeof val == 'boolean') {
			rv = this.renderBool(val);
		}
		return Ext.util.Format.htmlEncode(rv);
	},

	// private
	getPropertyName : function(name) {
		var pn = this.grid.propertyNames;
		return pn && pn[name] ? pn[name] : name;
	},

	// private
	getCellEditor : function(colIndex, rowIndex) {
		var p = this.store.getProperty(rowIndex), n = p.data.name, val = p.data.value;
		if (this.grid.customEditors[n]) {
			return this.grid.customEditors[n];
		}
		if (Ext.isDate(val)) {
			return this.editors.date;
		} else if (typeof val == 'number') {
			return this.editors.number;
		} else if (typeof val == 'boolean') {
			return this.editors['boolean'];
		} else {
			return this.editors.string;
		}
	},

	// inherit docs
	destroy : function() {
		Ext.ux.Bike.grid.PropertyColumnModel.superclass.destroy.call(this);
		for (var ed in this.editors) {
			Ext.destroy(this.editors[ed]);
		}
	}
});

/**
 * @class Ext.ux.Bike.grid.PropertyGrid
 * @extends Ext.grid.EditorGridPanel A specialized grid implementation intended
 *          to mimic the traditional property grid as typically seen in
 *          development IDEs. Each row in the grid represents a property of some
 *          object, and the data is stored as a set of name/value pairs in
 *          {@link Ext.grid.PropertyRecord}s. Example usage:
 * 
 * <pre><code>
 * var grid = new Ext.ux.Bike.grid.PropertyGrid({
 * 			title : 'Properties Grid',
 * 			autoHeight : true,
 * 			width : 300,
 * 			renderTo : 'grid-ct',
 * 			source : {
 * 				&quot;(name)&quot; : &quot;My Object&quot;,
 * 				&quot;Created&quot; : new Date(Date.parse('10/15/2006')),
 * 				&quot;Available&quot; : false,
 * 				&quot;Version&quot; : .01,
 * 				&quot;Description&quot; : &quot;A test object&quot;
 * 			}
 * 		});
 * </code></pre>
 * 
 * Customized to make name column unsortable
 * @constructor
 * @param {Object}
 *            config The grid config object
 */
Ext.ux.Bike.grid.PropertyGrid = Ext.extend(Ext.grid.EditorGridPanel, {
	/**
	 * @cfg {Object} propertyNames An object containing property name/display
	 *      name pairs. If specified, the display name will be shown in the name
	 *      column instead of the property name.
	 */
	/**
	 * @cfg {Object} source A data object to use as the data source of the grid
	 *      (see {@link #setSource} for details).
	 */
	/**
	 * @cfg {Object} customEditors An object containing name/value pairs of
	 *      custom editor type definitions that allow the grid to support
	 *      additional types of editable fields. By default, the grid supports
	 *      strongly-typed editing of strings, dates, numbers and booleans using
	 *      built-in form editors, but any custom type can be supported and
	 *      associated with a custom input control by specifying a custom
	 *      editor. The name of the editor type should correspond with the name
	 *      of the property that will use the editor. Example usage:
	 * 
	 * <pre><code>
	 * 	var grid = new Ext.ux.Bike.grid.PropertyGrid({
	 * 	...
	 * 	customEditors: {
	 * 	    'Start Time': new Ext.grid.GridEditor(new Ext.form.TimeField({selectOnFocus:true}))
	 * 	},
	 * 	source: {
	 * 	    'Start Time': '10:00 AM'
	 * 	}
	 * 	});
	 * 	
	 * </code></pre>
	 */
	/**
	 * @cfg {Object} source A data object to use as the data source of the grid
	 *      (see {@link #setSource} for details).
	 */
	/**
	 * @cfg {Object} customRenderers An object containing name/value pairs of
	 *      custom renderer type definitions that allow the grid to support
	 *      custom rendering of fields. By default, the grid supports
	 *      strongly-typed rendering of strings, dates, numbers and booleans
	 *      using built-in form editors, but any custom type can be supported
	 *      and associated with the type of the value. The name of the renderer
	 *      type should correspond with the name of the property that it will
	 *      render. Example usage:
	 * 
	 * <pre><code>
	 * 	var grid = new Ext.ux.Bike.grid.PropertyGrid({
	 * 	...
	 * 	customRenderers: {
	 * 	    Available: function(v){
	 * 	        if(v){
	 * 	            return '&lt;span style=&quot;color: green;&quot;&gt;Yes&lt;/span&gt;';
	 * 	        }else{
	 * 	            return '&lt;span style=&quot;color: red;&quot;&gt;No&lt;/span&gt;';
	 * 	        }
	 * 	    }
	 * 	},
	 * 	source: {
	 * 	    Available: true
	 * 	}
	 * 	});
	 * 	
	 * </code></pre>
	 */

	// private config overrides
	enableColumnMove : false,
	stripeRows : false,
	trackMouseOver : false,
	clicksToEdit : 1,
	enableHdMenu : false,
	viewConfig : {
		forceFit : true
	},

	// private
	initComponent : function() {
		this.customRenderers = this.customRenderers || {};
		this.customEditors = this.customEditors || {};
		this.lastEditRow = null;
		var store = new Ext.grid.PropertyStore(this);
		this.propStore = store;
		var cm = new Ext.ux.Bike.grid.PropertyColumnModel(this, store);
		this.addEvents(
				/**
				 * @event beforepropertychange Fires before a property value
				 *        changes. Handlers can return false to cancel the
				 *        property change (this will internally call
				 *        {@link Ext.data.Record#reject} on the property's
				 *        record).
				 * @param {Object}
				 *            source The source data object for the grid
				 *            (corresponds to the same object passed in as the
				 *            {@link #source} config property).
				 * @param {String}
				 *            recordId The record's id in the data store
				 * @param {Mixed}
				 *            value The current edited property value
				 * @param {Mixed}
				 *            oldValue The original property value prior to
				 *            editing
				 */
				'beforepropertychange',
				/**
				 * @event propertychange Fires after a property value has
				 *        changed.
				 * @param {Object}
				 *            source The source data object for the grid
				 *            (corresponds to the same object passed in as the
				 *            {@link #source} config property).
				 * @param {String}
				 *            recordId The record's id in the data store
				 * @param {Mixed}
				 *            value The current edited property value
				 * @param {Mixed}
				 *            oldValue The original property value prior to
				 *            editing
				 */
				'propertychange');
		this.cm = cm;
		this.ds = store.store;
		Ext.ux.Bike.grid.PropertyGrid.superclass.initComponent.call(this);

		this.mon(this.selModel, 'beforecellselect', function(sm, rowIndex,
						colIndex) {
					if (colIndex === 0) {
						this.startEditing.defer(200, this, [rowIndex, 1]);
						return false;
					}
				}, this);
	},

	// private
	onRender : function() {
		Ext.ux.Bike.grid.PropertyGrid.superclass.onRender.apply(this, arguments);

		this.getGridEl().addClass('x-props-grid');
	},

	// private
	afterRender : function() {
		Ext.ux.Bike.grid.PropertyGrid.superclass.afterRender.apply(this,
				arguments);
		if (this.source) {
			this.setSource(this.source);
		}
	},

	/**
	 * Sets the source data object containing the property data. The data object
	 * can contain one or more name/value pairs representing all of the
	 * properties of an object to display in the grid, and this data will
	 * automatically be loaded into the grid's {@link #store}. The values
	 * should be supplied in the proper data type if needed, otherwise string
	 * type will be assumed. If the grid already contains data, this method will
	 * replace any existing data. See also the {@link #source} config value.
	 * Example usage:
	 * 
	 * <pre><code>
	 * grid.setSource({
	 * 			&quot;(name)&quot; : &quot;My Object&quot;,
	 * 			&quot;Created&quot; : new Date(Date.parse('10/15/2006')), // date type
	 * 			&quot;Available&quot; : false, // boolean type
	 * 			&quot;Version&quot; : .01, // decimal type
	 * 			&quot;Description&quot; : &quot;A test object&quot;
	 * 		});
	 * </code></pre>
	 * 
	 * @param {Object}
	 *            source The data object
	 */
	setSource : function(source) {
		this.propStore.setSource(source);
	},

	/**
	 * Gets the source data object containing the property data. See
	 * {@link #setSource} for details regarding the format of the data object.
	 * 
	 * @return {Object} The data object
	 */
	getSource : function() {
		return this.propStore.getSource();
	},

	/**
	 * Sets the value of a property.
	 * 
	 * @param {String}
	 *            prop The name of the property to set
	 * @param {Mixed}
	 *            value The value to test
	 * @param {Boolean}
	 *            create (Optional) True to create the property if it doesn't
	 *            already exist. Defaults to <tt>false</tt>.
	 */
	setProperty : function(prop, value, create) {
		this.propStore.setValue(prop, value, create);
	},

	/**
	 * Removes a property from the grid.
	 * 
	 * @param {String}
	 *            prop The name of the property to remove
	 */
	removeProperty : function(prop) {
		this.propStore.remove(prop);
	}

		/**
		 * @cfg store
		 * @hide
		 */
		/**
		 * @cfg colModel
		 * @hide
		 */
		/**
		 * @cfg cm
		 * @hide
		 */
		/**
		 * @cfg columns
		 * @hide
		 */
});